package chessapp;

//
//  Board.java
//  ChessApp
//
//  Created by Peter Hunter on Sun Dec 30 2001.
//  Java version copyright (c) 2001 Peter Hunter. All rights reserved.
//  This code is heavily based on Tom Kerrigan's tscp, for which he
//  owns the copyright, and is used with his permission. All rights are
//  reserved by the owners of the respective copyrights.

import java.util.*;

final public class Board {
    final static int LIGHT = 0;
    final static int DARK = 1;
    final static int PAWN = 0;
    final static int KNIGHT = 1;
    final static int BISHOP = 2;
    final static int ROOK = 3;
    final static int QUEEN = 4;
    final static int KING = 5;
    final static int EMPTY = 6;

    final static char A1 = 56;
    final static char B1 = 57;
    final static char C1 = 58;
    final static char D1 = 59;
    final static char E1 = 60;
    final static char F1 = 61;
    final static char G1 = 62;
    final static char H1 = 63;
    final static char A8 = 0;
    final static char B8 = 1;
    final static char C8 = 2;
    final static char D8 = 3;
    final static char E8 = 4;
    final static char F8 = 5;
    final static char G8 = 6;
    final static char H8 = 7;

    final static int DOUBLED_PAWN_PENALTY = 10;
    final static int ISOLATED_PAWN_PENALTY = 20;
    final static int BACKWARDS_PAWN_PENALTY = 8;
    final static int PASSED_PAWN_BONUS = 20;
    final static int ROOK_SEMI_OPEN_FILE_BONUS = 10;
    final static int ROOK_OPEN_FILE_BONUS = 15;
    final static int ROOK_ON_SEVENTH_BONUS = 20;

    final static int HIST_STACK = 400;

    int side = LIGHT;
    int xside = DARK;
    int castle = 15;
    int ep = -1;
    int fifty = 0;
    int hply = 0;
    int history[][] = new int[64][64];
    HistoryData histDat[] = new HistoryData[HIST_STACK];
    int pawnRank[][] = new int [2][10];
    int pieceMat[] = new int[2];
    int pawnMat[] = new int[2];
    
    int color[] =  {
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0
    };
    
    int piece[] =  {
	3, 1, 2, 4, 5, 2, 1, 3,
	0, 0, 0, 0, 0, 0, 0, 0,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6,
	0, 0, 0, 0, 0, 0, 0, 0,
	3, 1, 2, 4, 5, 2, 1, 3
    };
    
    final char pieceChar[] = { 'P', 'N', 'B', 'R', 'Q', 'K' };
    
    private boolean slide[] = { false, false, true, true, true, false };

    private int offsets[] = { 0, 8, 4, 4, 8, 8 };
    
    private int offset[][] = {
        { 0, 0, 0, 0, 0, 0, 0, 0 },
        { -21, -19, -12, -8, 8, 12, 19, 21 },
        { -11, -9, 9, 11, 0, 0, 0, 0 },
        { -10, -1, 1, 10, 0, 0, 0, 0 },
        { -11, -10, -9, -1, 1, 9, 10, 11 },
        { -11, -10, -9, -1, 1, 9, 10, 11 }
    };
    
    int mailbox[] = {
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
         -1,  0,  1,  2,  3,  4,  5,  6,  7, -1,
         -1,  8,  9, 10, 11, 12, 13, 14, 15, -1,
         -1, 16, 17, 18, 19, 20, 21, 22, 23, -1,
         -1, 24, 25, 26, 27, 28, 29, 30, 31, -1,
         -1, 32, 33, 34, 35, 36, 37, 38, 39, -1,
         -1, 40, 41, 42, 43, 44, 45, 46, 47, -1,
         -1, 48, 49, 50, 51, 52, 53, 54, 55, -1,
         -1, 56, 57, 58, 59, 60, 61, 62, 63, -1,
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    };

    int mailbox64[] = {
        21, 22, 23, 24, 25, 26, 27, 28,
        31, 32, 33, 34, 35, 36, 37, 38,
        41, 42, 43, 44, 45, 46, 47, 48,
        51, 52, 53, 54, 55, 56, 57, 58,
        61, 62, 63, 64, 65, 66, 67, 68,
        71, 72, 73, 74, 75, 76, 77, 78,
        81, 82, 83, 84, 85, 86, 87, 88,
        91, 92, 93, 94, 95, 96, 97, 98
    };
    
    int castleMask[] = {
         7, 15, 15, 15,  3, 15, 15, 11,
        15, 15, 15, 15, 15, 15, 15, 15,
        15, 15, 15, 15, 15, 15, 15, 15,
        15, 15, 15, 15, 15, 15, 15, 15,
        15, 15, 15, 15, 15, 15, 15, 15,
        15, 15, 15, 15, 15, 15, 15, 15,
        15, 15, 15, 15, 15, 15, 15, 15,
        13, 15, 15, 15, 12, 15, 15, 14
    };
    
    /* the values of the pieces */
    int pieceValue[] = {
            100, 300, 300, 500, 900, 0
    };
    
    /* The "pcsq" arrays are piece/square tables. They're values
    added to the material value of the piece based on the
    location of the piece. */
    
    int pawnPcsq[] = {
            0,   0,   0,   0,   0,   0,   0,   0,
            5,  10,  15,  20,  20,  15,  10,   5,
            4,   8,  12,  16,  16,  12,   8,   4,
            3,   6,   9,  12,  12,   9,   6,   3,
            2,   4,   6,   8,   8,   6,   4,   2,
            1,   2,   3, -10, -10,   3,   2,   1,
            0,   0,   0, -40, -40,   0,   0,   0,
            0,   0,   0,   0,   0,   0,   0,   0
    };
    
    int knightPcsq[] = {
            -10, -10, -10, -10, -10, -10, -10, -10,
            -10,   0,   0,   0,   0,   0,   0, -10,
            -10,   0,   5,   5,   5,   5,   0, -10,
            -10,   0,   5,  10,  10,   5,   0, -10,
            -10,   0,   5,  10,  10,   5,   0, -10,
            -10,   0,   5,   5,   5,   5,   0, -10,
            -10,   0,   0,   0,   0,   0,   0, -10,
            -10, -30, -10, -10, -10, -10, -30, -10
    };
    
    int bishopPcsq[] = {
            -10, -10, -10, -10, -10, -10, -10, -10,
            -10,   0,   0,   0,   0,   0,   0, -10,
            -10,   0,   5,   5,   5,   5,   0, -10,
            -10,   0,   5,  10,  10,   5,   0, -10,
            -10,   0,   5,  10,  10,   5,   0, -10,
            -10,   0,   5,   5,   5,   5,   0, -10,
            -10,   0,   0,   0,   0,   0,   0, -10,
            -10, -10, -20, -10, -10, -20, -10, -10
    };
    
    int kingPcsq[] = {
            -40, -40, -40, -40, -40, -40, -40, -40,
            -40, -40, -40, -40, -40, -40, -40, -40,
            -40, -40, -40, -40, -40, -40, -40, -40,
            -40, -40, -40, -40, -40, -40, -40, -40,
            -40, -40, -40, -40, -40, -40, -40, -40,
            -40, -40, -40, -40, -40, -40, -40, -40,
            -20, -20, -20, -20, -20, -20, -20, -20,
              0,  20,  40, -20,   0, -20,  40,  20
    };
    
    int kingEndgamePcsq[] = {
             0,  10,  20,  30,  30,  20,  10,   0,
            10,  20,  30,  40,  40,  30,  20,  10,
            20,  30,  40,  50,  50,  40,  30,  20,
            30,  40,  50,  60,  60,  50,  40,  30,
            30,  40,  50,  60,  60,  50,  40,  30,
            20,  30,  40,  50,  50,  40,  30,  20,
            10,  20,  30,  40,  40,  30,  20,  10,
             0,  10,  20,  30,  30,  20,  10,   0
    };
    
    /* The flip array is used to calculate the piece/square
    values for DARK pieces. The piece/square value of a
    LIGHT pawn is pawnPcsq[sq] and the value of a DARK
    pawn is pawnPcsq[flip[sq]] */
    int flip[] = {
            56,  57,  58,  59,  60,  61,  62,  63,
            48,  49,  50,  51,  52,  53,  54,  55,
            40,  41,  42,  43,  44,  45,  46,  47,
            32,  33,  34,  35,  36,  37,  38,  39,
            24,  25,  26,  27,  28,  29,  30,  31,
            16,  17,  18,  19,  20,  21,  22,  23,
            8,   9,  10,  11,  12,  13,  14,  15,
            0,   1,   2,   3,   4,   5,   6,   7
    };
    
    public int getColor(int i, int j) {
        return color[(i << 3) + j];
    }
    
    public int getPiece(int i, int j) {
        return piece[(i << 3) + j];
    }
    
    public boolean isWhiteToMove() {
        return (side == LIGHT);
    }
    
    /* inCheck() returns true if side s is in check and false
    otherwise. It just scans the board to find side s's king
    and calls attack() to see if it's being attacked. */

    boolean inCheck(int s) {
        int i;
    
        for (i = 0; i < 64; ++i)
            if (piece[i] == KING && color[i] == s)
                return attack(i, s ^ 1);
        return true;  /* shouldn't get here */
    }
    

    /* attack() returns true if square sq is being attacked by side
    s and false otherwise. */
    
    boolean attack(int sq, int s) {
	int i, j, n;

	for (i = 0; i < 64; ++i)
            if (color[i] == s) {
                int p = piece[i];
                if (p == PAWN) {
                    if (s == LIGHT) {
                        if (COL(i) != 0 && i - 9 == sq)
                            return true;
                        if (COL(i) != 7 && i - 7 == sq)
                            return true;
                    }
                    else {
                        if (COL(i) != 0 && i + 7 == sq)
                            return true;
                        if (COL(i) != 7 && i + 9 == sq)
                            return true;
                    }
                }
                else
                    for (j = 0; j < offsets[p]; ++j)
                        for (n = i;;) {
                            n = mailbox[mailbox64[n] + offset[p][j]];
                            if (n == -1)
                                break;
                            if (n == sq)
                                return true;
                            if (color[n] != EMPTY)
                                break;
                            if (!slide[p])
                                break;
                        }
            }
	return false;
    }


    /* gen() generates pseudo-legal moves for the current position.
    It scans the board to find friendly pieces and then determines
    what squares they attack. When it finds a piece/square
    combination, it calls genPush to put the move on the "move
    stack." */
    
    TreeSet<Move> gen() {
        TreeSet<Move> ret = new TreeSet<Move>();
        
	for (int i = 0; i < 64; ++i)
            if (color[i] == side) {
                if (piece[i] == PAWN) {
                    if (side == LIGHT) {
                        if (COL(i) != 0 && color[i - 9] == DARK)
                            genPush(ret, i, i - 9, 17);
                        if (COL(i) != 7 && color[i - 7] == DARK)
                            genPush(ret, i, i - 7, 17);
                        if (color[i - 8] == EMPTY) {
                            genPush(ret, i, i - 8, 16);
                            if (i >= 48 && color[i - 16] == EMPTY)
                                genPush(ret, i, i - 16, 24);
                        }
                    }
                    else {
                        if (COL(i) != 0 && color[i + 7] == LIGHT)
                            genPush(ret, i, i + 7, 17);
                        if (COL(i) != 7 && color[i + 9] == LIGHT)
                            genPush(ret, i, i + 9, 17);
                        if (color[i + 8] == EMPTY) {
                            genPush(ret, i, i + 8, 16);
                            if (i <= 15 && color[i + 16] == EMPTY)
                                genPush(ret, i, i + 16, 24);
                        }
                    }
                }
                else
                    for (int j = 0; j < offsets[piece[i]]; ++j)
                        for (int n = i;;) {
                            n = mailbox[mailbox64[n] + offset[piece[i]][j]];
                            if (n == -1)
                                break;
                            if (color[n] != EMPTY) {
                                if (color[n] == xside)
                                    genPush(ret, i, n, 1);
                                break;
                            }
                            genPush(ret, i, n, 0);
                            if (!slide[piece[i]])
                                break;
                        }
            }

	/* generate castle moves */
	if (side == LIGHT) {
            if ((castle & 1) != 0)
                genPush(ret, E1, G1, 2);
            if ((castle & 2) != 0)
                genPush(ret, E1, C1, 2);
	}
	else {
            if ((castle & 4) != 0)
                genPush(ret, E8, G8, 2);
            if ((castle & 8) != 0)
                genPush(ret, E8, C8, 2);
	}
	
	/* generate en passant moves */
	if (ep != -1) {
            if (side == LIGHT) {
                if (COL(ep) != 0 && color[ep + 7] == LIGHT && piece[ep + 7] == PAWN)
                    genPush(ret, ep + 7, ep, 21);
                if (COL(ep) != 7 && color[ep + 9] == LIGHT && piece[ep + 9] == PAWN)
                    genPush(ret, ep + 9, ep, 21);
            }
            else {
                if (COL(ep) != 0 && color[ep - 9] == DARK && piece[ep - 9] == PAWN)
                    genPush(ret, ep - 9, ep, 21);
                if (COL(ep) != 7 && color[ep - 7] == DARK && piece[ep - 7] == PAWN)
                    genPush(ret, ep - 7, ep, 21);
            }
	}
        return ret;
    }


/* genCaps() is basically a copy of gen() that's modified to
   only generate capture and promote moves. It's used by the
   quiescence search. */

    TreeSet<Move> genCaps() {
        TreeSet<Move> ret = new TreeSet<Move>();

	for (int i = 0; i < 64; ++i)
            if (color[i] == side) {
                if (piece[i]==PAWN) {
                    if (side == LIGHT) {
                        if (COL(i) != 0 && color[i - 9] == DARK)
                            genPush(ret, i, i - 9, 17);
                        if (COL(i) != 7 && color[i - 7] == DARK)
                            genPush(ret, i, i - 7, 17);
                        if (i <= 15 && color[i - 8] == EMPTY)
                            genPush(ret, i, i - 8, 16);
                    }
                    if (side == DARK) {
                        if (COL(i) != 0 && color[i + 7] == LIGHT)
                            genPush(ret, i, i + 7, 17);
                        if (COL(i) != 7 && color[i + 9] == LIGHT)
                            genPush(ret, i, i + 9, 17);
                        if (i >= 48 && color[i + 8] == EMPTY)
                            genPush(ret, i, i + 8, 16);
                    }
                }
                else
                    for (int j = 0; j < offsets[piece[i]]; ++j)
                        for (int n = i;;) {
                            n = mailbox[mailbox64[n] + offset[piece[i]][j]];
                            if (n == -1)
                                break;
                            if (color[n] != EMPTY) {
                                if (color[n] == xside)
                                    genPush(ret, i, n, 1);
                                break;
                            }
                            if (!slide[piece[i]])
                                break;
                        }
            }
	if (ep != -1) {
            if (side == LIGHT) {
                if (COL(ep) != 0 && color[ep + 7] == LIGHT && piece[ep + 7] == PAWN)
                    genPush(ret, ep + 7, ep, 21);
                if (COL(ep) != 7 && color[ep + 9] == LIGHT && piece[ep + 9] == PAWN)
                    genPush(ret, ep + 9, ep, 21);
            }
            else {
                if (COL(ep) != 0 && color[ep - 9] == DARK && piece[ep - 9] == PAWN)
                    genPush(ret, ep - 9, ep, 21);
                if (COL(ep) != 7 && color[ep - 7] == DARK && piece[ep - 7] == PAWN)
                    genPush(ret, ep - 7, ep, 21);
            }
	}
        return ret;
    }

    /* genPush() puts a move on the move stack, unless it's a
    pawn promotion that needs to be handled by genPromote().
    It also assigns a score to the move for alpha-beta move
    ordering. If the move is a capture, it uses MVV/LVA
    (Most Valuable Victim/Least Valuable Attacker). Otherwise,
    it uses the move's history heuristic value. Note that
    1,000,000 is added to a capture move's score, so it
    always gets ordered above a "normal" move. */
    
    void genPush(TreeSet<Move> ret, int from, int to, int bits) {
	if ((bits & 16) != 0) {
            if (side == LIGHT) {
                if (to <= H8) {
                    genPromote(ret, from, to, bits);
                    return;
                }
            }
            else {
                if (to >= A1) {
                    genPromote(ret, from, to, bits);
                    return;
                }
            }
	}

        Move g = new Move(from, to, 0, bits);
        
	if (color[to] != EMPTY)
            g.setScore(1000000 + (piece[to] * 10) - piece[from]);
	else
            g.setScore(history[from][to]);
        ret.add(g);
    }


    /* genPromote() is just like genPush(), only it puts 4 moves
    on the move stack, one for each possible promotion piece */
    
    void genPromote(TreeSet<Move> ret, int from, int to, int bits) {
	for (char i = KNIGHT; i <= QUEEN; ++i) {
            Move g = new Move(from, to, i, (bits | 32));
            g.setScore(1000000 + (i * 10));
            ret.add(g);
	}
    }

    /* makemove() makes a move. If the move is illegal, it
    undoes whatever it did and returns false. Otherwise, it
    returns true. */
    
    boolean makeMove(Move m) {
	
	/* test to see if a castle move is legal and move the rook
	   (the king is moved with the usual move code later) */
	if ((m.bits & 2) != 0) {
            int from, to;

            if (inCheck(side))
                return false;
            switch (m.to) {
                case 62:
                    if (color[F1] != EMPTY || color[G1] != EMPTY ||
                            attack(F1, xside) || attack(G1, xside))
                        return false;
                    from = H1;
                    to = F1;
                    break;
                case 58:
                    if (color[B1] != EMPTY || color[C1] != EMPTY || color[D1] != EMPTY ||
                            attack(C1, xside) || attack(D1, xside))
                        return false;
                    from = A1;
                    to = D1;
                    break;
                case 6:
                    if (color[F8] != EMPTY || color[G8] != EMPTY ||
                            attack(F8, xside) || attack(G8, xside))
                        return false;
                    from = H8;
                    to = F8;
                    break;
                case 2:
                    if (color[B8] != EMPTY || color[C8] != EMPTY || color[D8] != EMPTY ||
                            attack(C8, xside) || attack(D8, xside))
                        return false;
                    from = A8;
                    to = D8;
                    break;
                default:  /* shouldn't get here */
                    from = -1;
                    to = -1;
                    break;
            }
            color[to] = color[from];
            piece[to] = piece[from];
            color[from] = EMPTY;
            piece[from] = EMPTY;
	}

	/* back up information so we can take the move back later. */
        histDat[hply] = new HistoryData();
	histDat[hply].m = m;
	histDat[hply].capture = piece[m.to];
	histDat[hply].castle = castle;
	histDat[hply].ep = ep;
	histDat[hply].fifty = fifty;
	++hply;

	/* update the castle, en passant, and
	   fifty-move-draw variables */
	castle &= castleMask[m.from] & castleMask[m.to];
	if ((m.bits & 8) != 0) {
            if (side == LIGHT)
                ep = m.to + 8;
            else
                ep = m.to - 8;
	}
	else
            ep = -1;
	if ((m.bits & 17) != 0)
            fifty = 0;
	else
            ++fifty;

	/* move the piece */
	color[m.to] = side;
	if ((m.bits & 32) != 0)
            piece[m.to] = m.promote;
	else
            piece[m.to] = piece[m.from];
	color[m.from] = EMPTY;
	piece[m.from] = EMPTY;

	/* erase the pawn if this is an en passant move */
	if ((m.bits & 4) != 0) {
            if (side == LIGHT) {
                color[m.to + 8] = EMPTY;
                piece[m.to + 8] = EMPTY;
            }
            else {
                color[m.to - 8] = EMPTY;
                piece[m.to - 8] = EMPTY;
            }
        }

	/* switch sides and test for legality (if we can capture
	   the other guy's king, it's an illegal position and
	   we need to take the move back) */
	side ^= 1;
	xside ^= 1;
	if (inCheck(xside)) {
            takeBack();
            return false;
	}
	return true;
    }


/* takeBack() is very similar to makeMove(), only backwards :)  */

    void takeBack() {
	side ^= 1;
	xside ^= 1;
	--hply;
	Move m = histDat[hply].m;
	castle = histDat[hply].castle;
	ep = histDat[hply].ep;
	fifty = histDat[hply].fifty;
	color[m.from] = side;
	if ((m.bits & 32) != 0)
            piece[m.from] = PAWN;
	else
            piece[m.from] = piece[m.to];
	if (histDat[hply].capture == EMPTY) {
            color[m.to] = EMPTY;
            piece[m.to] = EMPTY;
	}
	else {
            color[m.to] = xside;
            piece[m.to] = histDat[hply].capture;
	}
	if ((m.bits & 2) != 0) {
            int from, to;

            switch(m.to) {
                case 62:
                    from = F1;
                    to = H1;
                    break;
                case 58:
                    from = D1;
                    to = A1;
                    break;
                case 6:
                    from = F8;
                    to = H8;
                    break;
                case 2:
                    from = D8;
                    to = A8;
                    break;
                default:  /* shouldn't get here */
                    from = -1;
                    to = -1;
                    break;
            }
            color[to] = side;
            piece[to] = ROOK;
            color[from] = EMPTY;
            piece[from] = EMPTY;
	}
	if ((m.bits & 4) != 0) {
            if (side == LIGHT) {
                color[m.to + 8] = xside;
                piece[m.to + 8] = PAWN;
            }
            else {
                color[m.to - 8] = xside;
                piece[m.to - 8] = PAWN;
            }
	}
    }
    
    @Override
    public String toString() {
	int i;
	
        StringBuffer sb = new StringBuffer("\n8 ");
	for (i = 0; i < 64; ++i) {
            switch (color[i]) {
                case EMPTY:
                    sb.append(" .");
                    break;
                case LIGHT:
                    sb.append(" ");
                    sb.append(pieceChar[piece[i]]);
                    break;
                case DARK:
                    sb.append(" ");
                    sb.append((char) (pieceChar[piece[i]] + ('a' - 'A')));
                    break;
                default:
                    throw new IllegalStateException("Square not EMPTY, LIGHT or DARK: " + i);
            }
            if ((i + 1) % 8 == 0 && i != 63) {
                sb.append("\n");
                sb.append(Integer.toString(7 - ROW(i)));
                sb.append(" ");
            }
	}
	sb.append("\n\n   a b c d e f g h\n\n");
        return sb.toString();
    }

    /* reps() returns the number of times that the current
    position has been repeated. Thanks to John Stanback
    for this clever algorithm. */
    
    int reps() {
	int b[] = new int[64];
	int c = 0;  /* count of squares that are different from
				   the current position */
	int r = 0;  /* number of repetitions */

	/* is a repetition impossible? */
	if (fifty <= 3)
		return 0;

	/* loop through the reversible moves */
	for (int i = hply - 1; i >= hply - fifty - 1; --i) {
            if (++b[histDat[i].m.from] == 0)
                --c;
            else
                ++c;
            if (--b[histDat[i].m.to] == 0)
                --c;
            else
                ++c;
            if (c == 0)
                ++r;
	}

	return r;
    }

    int eval() {
	int score[] = new int[2];  /* each side's score */

	/* this is the first pass: set up pawnRank, pieceMat, and pawnMat. */
	for (int i = 0; i < 10; ++i) {
		pawnRank[LIGHT][i] = 0;
		pawnRank[DARK][i] = 7;
	}
	pieceMat[LIGHT] = 0;
	pieceMat[DARK] = 0;
	pawnMat[LIGHT] = 0;
	pawnMat[DARK] = 0;
	for (int i = 0; i < 64; ++i) {
            if (color[i] == EMPTY)
                continue;
            if (piece[i] == PAWN) {
                pawnMat[color[i]] += pieceValue[PAWN];
                int f = COL(i) + 1;  /* add 1 because of the extra file in the array */
                if (color[i] == LIGHT) {
                    if (pawnRank[LIGHT][f] < ROW(i))
                        pawnRank[LIGHT][f] = ROW(i);
                }
                else {
                    if (pawnRank[DARK][f] > ROW(i))
                        pawnRank[DARK][f] = ROW(i);
                    }
		}
		else
                    pieceMat[color[i]] += pieceValue[piece[i]];
	}

	/* this is the second pass: evaluate each piece */
	score[LIGHT] = pieceMat[LIGHT] + pawnMat[LIGHT];
	score[DARK] = pieceMat[DARK] + pawnMat[DARK];
	for (int i = 0; i < 64; ++i) {
            if (color[i] == EMPTY)
                continue;
            if (color[i] == LIGHT) {
                switch (piece[i]) {
                    case PAWN:
                        score[LIGHT] += evalLightPawn(i);
                        break;
                    case KNIGHT:
                        score[LIGHT] += knightPcsq[i];
                        break;
                    case BISHOP:
                        score[LIGHT] += bishopPcsq[i];
                        break;
                    case ROOK:
                        if (pawnRank[LIGHT][COL(i) + 1] == 0) {
                            if (pawnRank[DARK][COL(i) + 1] == 7)
                                score[LIGHT] += ROOK_OPEN_FILE_BONUS;
                            else
                                score[LIGHT] += ROOK_SEMI_OPEN_FILE_BONUS;
                        }
                        if (ROW(i) == 1)
                            score[LIGHT] += ROOK_ON_SEVENTH_BONUS;
                        break;
                    case KING:
                        if (pieceMat[DARK] <= 1200)
                            score[LIGHT] += kingEndgamePcsq[i];
                        else
                            score[LIGHT] += evalLightKing(i);
                        break;
                }
            }
            else {
                switch (piece[i]) {
                    case PAWN:
                        score[DARK] += evalDarkPawn(i);
                        break;
                    case KNIGHT:
                        score[DARK] += knightPcsq[flip[i]];
                        break;
                    case BISHOP:
                        score[DARK] += bishopPcsq[flip[i]];
                        break;
                    case ROOK:
                        if (pawnRank[DARK][COL(i) + 1] == 7) {
                            if (pawnRank[LIGHT][COL(i) + 1] == 0)
                                score[DARK] += ROOK_OPEN_FILE_BONUS;
                            else
                                score[DARK] += ROOK_SEMI_OPEN_FILE_BONUS;
                        }
                        if (ROW(i) == 6)
                            score[DARK] += ROOK_ON_SEVENTH_BONUS;
                        break;
                    case KING:
                        if (pieceMat[LIGHT] <= 1200)
                            score[DARK] += kingEndgamePcsq[flip[i]];
                        else
                            score[DARK] += evalDarkKing(i);
                        break;
                }
            }
	}

	/* the score[] array is set, now return the score relative
	   to the side to move */
	if (side == LIGHT)
            return score[LIGHT] - score[DARK];
	return score[DARK] - score[LIGHT];
    }

    int evalLightPawn(int sq) {
	int r = 0; /* return value */
	int f = COL(sq) + 1; /* pawn's file */

	r += pawnPcsq[sq];

	/* if there's a pawn behind this one, it's doubled */
	if (pawnRank[LIGHT][f] > ROW(sq))
            r -= DOUBLED_PAWN_PENALTY;

	/* if there aren't any friendly pawns on either side of
	   this one, it's isolated */
	if ((pawnRank[LIGHT][f - 1] == 0) &&
                (pawnRank[LIGHT][f + 1] == 0))
            r -= ISOLATED_PAWN_PENALTY;

	/* if it's not isolated, it might be backwards */
	else if ((pawnRank[LIGHT][f - 1] < ROW(sq)) &&
                (pawnRank[LIGHT][f + 1] < ROW(sq)))
            r -= BACKWARDS_PAWN_PENALTY;

	/* add a bonus if the pawn is passed */
	if ((pawnRank[DARK][f - 1] >= ROW(sq)) &&
                (pawnRank[DARK][f] >= ROW(sq)) &&
                (pawnRank[DARK][f + 1] >= ROW(sq)))
            r += (7 - ROW(sq)) * PASSED_PAWN_BONUS;

        return r;
    }

int evalDarkPawn(int sq) {
	int r = 0;  /* the value to return */
	int f = COL(sq) + 1;  /* the pawn's file */

	r += pawnPcsq[flip[sq]];

	/* if there's a pawn behind this one, it's doubled */
	if (pawnRank[DARK][f] < ROW(sq))
            r -= DOUBLED_PAWN_PENALTY;

	/* if there aren't any friendly pawns on either side of
	   this one, it's isolated */
	if ((pawnRank[DARK][f - 1] == 7) &&
                (pawnRank[DARK][f + 1] == 7))
            r -= ISOLATED_PAWN_PENALTY;

	/* if it's not isolated, it might be backwards */
	else if ((pawnRank[DARK][f - 1] > ROW(sq)) &&
                (pawnRank[DARK][f + 1] > ROW(sq)))
            r -= BACKWARDS_PAWN_PENALTY;

	/* add a bonus if the pawn is passed */
	if ((pawnRank[LIGHT][f - 1] <= ROW(sq)) &&
                (pawnRank[LIGHT][f] <= ROW(sq)) &&
                (pawnRank[LIGHT][f + 1] <= ROW(sq)))
            r += ROW(sq) * PASSED_PAWN_BONUS;

	return r;
    }

    int evalLightKing(int sq) {
	int r = kingPcsq[sq]; /* return value */

	/* if the king is castled, use a special function to evaluate the
	   pawns on the appropriate side */
	if (COL(sq) < 3) {
            r += evalLkp(1);
            r += evalLkp(2);
            r += evalLkp(3) / 2;  /* problems with pawns on the c & f files
                                                                are not as severe */
	}
	else if (COL(sq) > 4) {
            r += evalLkp(8);
            r += evalLkp(7);
            r += evalLkp(6) / 2;
	}

	/* otherwise, just assess a penalty if there are open files near
	   the king */
	else {
            for (int i = COL(sq); i <= COL(sq) + 2; ++i)
                if ((pawnRank[LIGHT][i] == 0) &&
                        (pawnRank[DARK][i] == 7))
                    r -= 10;
	}

	/* scale the king safety value according to the opponent's material;
	   the premise is that your king safety can only be bad if the
	   opponent has enough pieces to attack you */
	r *= pieceMat[DARK];
	r /= 3100;

	return r;
    }

    /* evalLkp(f) evaluates the Light King Pawn on file f */

    @SuppressWarnings("empty-statement")
    int evalLkp(int f) {
	int r = 0;

	if (pawnRank[LIGHT][f] == 6);  /* pawn hasn't moved */
	else if (pawnRank[LIGHT][f] == 5)
            r -= 10;  /* pawn moved one square */
	else if (pawnRank[LIGHT][f] != 0)
            r -= 20;  /* pawn moved more than one square */
	else
            r -= 25;  /* no pawn on this file */

	if (pawnRank[DARK][f] == 7)
            r -= 15;  /* no enemy pawn */
	else if (pawnRank[DARK][f] == 5)
            r -= 10;  /* enemy pawn on the 3rd rank */
	else if (pawnRank[DARK][f] == 4)
            r -= 5;   /* enemy pawn on the 4th rank */

	return r;
    }

    int evalDarkKing(int sq) {
	int r;
	int i;

	r = kingPcsq[flip[sq]];
	if (COL(sq) < 3) {
            r += evalDkp(1);
            r += evalDkp(2);
            r += evalDkp(3) / 2;
	}
	else if (COL(sq) > 4) {
            r += evalDkp(8);
            r += evalDkp(7);
            r += evalDkp(6) / 2;
	}
	else {
            for (i = COL(sq); i <= COL(sq) + 2; ++i)
                if ((pawnRank[LIGHT][i] == 0) &&
                        (pawnRank[DARK][i] == 7))
                    r -= 10;
	}
	r *= pieceMat[LIGHT];
	r /= 3100;
	return r;
    }

    @SuppressWarnings("empty-statement")
    int evalDkp(int f) {
	int r = 0;

	if (pawnRank[DARK][f] == 1);
	else if (pawnRank[DARK][f] == 2)
            r -= 10;
	else if (pawnRank[DARK][f] != 7)
            r -= 20;
	else
            r -= 25;

	if (pawnRank[LIGHT][f] == 0)
            r -= 15;
	else if (pawnRank[LIGHT][f] == 2)
            r -= 10;
	else if (pawnRank[LIGHT][f] == 3)
            r -= 5;

	return r;
    }

    static int COL(int x) { return (x & 7); }
    static int ROW(int x) { return (x >> 3); }
}
